package com.funmi.lizip.security.filter;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.funmi.lizip.common.base.R;
import com.funmi.lizip.common.base.ResultCode;
import com.funmi.lizip.security.entity.SecurityAdminUser;
import com.funmi.lizip.security.entity.SecurityUser;
import com.funmi.lizip.security.entity.TokenPayLoad;
import com.funmi.lizip.security.util.JwtTokenUtil;
import com.nimbusds.jose.JOSEException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.ParseException;

@Slf4j
public class JwtTokenFilter extends GenericFilterBean {
    @Autowired
    private UserDetailsService userDetailsService;
    private String username;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;

        try {
            // 1. 获取认证头中的Token
            String jwtToken = request.getHeader("authorization");
            log.debug("后台检查令牌:{}", jwtToken);

            if (StrUtil.isNotBlank(jwtToken)) {
                // 1. 获取认证头中的Token
                jwtToken = jwtToken.replace("Bearer", "").trim();

                // 2. 校验Token是否有效
                // 3. 从Token中获取TokenPayLoad
                // 4. 判断Token是否超期
                TokenPayLoad tokenPayLoad = JwtTokenUtil.verifyTokenByRSA(jwtToken);

                // 5. 获得登录名，然后恢复安全环境（SecurityUser）
                username = tokenPayLoad.getSub();
                // 从数据库中查询用户信息
                UserDetails securityUser = null;
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                Class<UserDetails> userDetailsClass = UserDetails.class;
                if (userDetails instanceof SecurityUser){
                    securityUser = (SecurityUser) userDetails;
                }else{
                    securityUser = (SecurityAdminUser) userDetails;
                }
                // 创建UsernamePasswordAuthenticationToken
                UsernamePasswordAuthenticationToken token =
                        new UsernamePasswordAuthenticationToken(username, null, securityUser.getAuthorities());
                // 恢复用户的安全环境
                SecurityContextHolder.getContext().setAuthentication(token);
            }

            // 6. 请求转到下一个目标
            filterChain.doFilter(request, servletResponse);

        } catch (AuthenticationException | ParseException | JOSEException e) {
            // 清除安全环境
            SecurityContextHolder.clearContext();
            // 认证失败，则重新认证。
            // 注意：这里异常不能被配置的authenticationEntryPoint所处理
            response.setContentType("application/json;charset=utf-8");
            // 401 表示要求客户端认证
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            // 返回错误消息
            ObjectMapper objectMapper = new ObjectMapper();
            R result = R.fail().code(ResultCode.LOGIN_AUTH).message("用户未登录或Token超期，请重新登录?");
            response.getWriter().write(objectMapper.writeValueAsString(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
